/*                                                                      
 Copyright 2022 hzheng@gzhu.edu.cn              
                                                                         
 Licensed under the Apache License, Version 2.0 (the "License");         
 you may not use this file except in compliance with the License.        
 You may obtain a copy of the License at                                 
                                                                         
     http://www.apache.org/licenses/LICENSE-2.0                          
                                                                         
  Unless required by applicable law or agreed to in writing, software    
 distributed under the License is distributed on an "AS IS" BASIS,       
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and     
 limitations under the License.                                          
 */                                                                      
/*+***********************************************************
Filename: mcu.v
Description: a simple mcu.
Modification:
  2021.10.07 creation by Zheng Hui (03_CPU)
  2021.12.28 add Hardware Loader and UART (06_MCU_Tiny)
  2022.02.06 add RAM&Load/Store (06_v02_MCU_Tiny)

************************************************************-*/

`include "e203_defines.v"

module mcu
	(
		input wire CLK_IN,	
        input wire RST_N,
        input wire RXD,        
        output wire TXD,             
		output wire LED0,        
		output wire LED1,        
		output wire LED2
	);
		
    wire HW_Loader_Reset;		
    wire core_RST_N = (~HW_Loader_Reset) & RST_N;	

    reg [25:0] pre_cnt;

	initial
	begin
		pre_cnt=26'b0;
	end		
	
    //PLL, produce sys_clk
    //    
    wire clk_192M;	    
	wire clk_96M;	
		
	PLL clkpll(		
		.refclk (CLK_IN),	
		.reset (~RST_N),	
		.clk0_out (clk_96M),	//96MHz	
		.clk1_out (clk_192M)		//192MHz
	);

    //produce core_clk
    //by prescale sys_clk    
//    reg [3:0] addr;    

    
//    always@(posedge CLK_IN or negedge RST_N) begin
    always@(posedge CLK_IN or negedge core_RST_N) begin
//          if (~RST_N) begin
          if (~core_RST_N) begin
            pre_cnt<=0;
 //           addr<=0;
          end
          else begin
            pre_cnt<=pre_cnt+1;
  //          if (pre_cnt==0) begin
  //            addr<=addr+1;
  //          end
          end
        end    
        
//    wire core_clk = pre_cnt[20];            
    wire core_clk = pre_cnt[12];            
		
    /**		
     * core part    
     */
    wire [31:0] pc_value;
    wire [31:0] instruction;


	
	//
    //Program Counter
    //  
    wire [31:0] pc_r;
    wire [31:0] pc_next;

    //PC as 32 D flip-flops
    //triggered by clk_PC which is a reset-delay version of core_clk   
    reg clk_PC_en;    
    always@(posedge core_clk or negedge core_RST_N) begin
          if (~core_RST_N) begin
            clk_PC_en<=0;
          end
          else begin
            clk_PC_en<=1;
          end
        end    
    wire clk_PC = clk_PC_en ? core_clk : 1'b1;        

//    sirv_gnrl_dfflr #(32) pc_dfflr (1'b1, pc_next, pc_r, core_clk, RST_N);
//    sirv_gnrl_dfflr #(32) pc_dfflr (1'b1, pc_next, pc_r, core_clk, core_RST_N);
    sirv_gnrl_dfflr #(32) pc_dfflr (1'b1, pc_next, pc_r, clk_PC, core_RST_N);
    assign pc_value = pc_r;        
    
    //
	//Decoder	    
    //
	wire [`E203_DECINFO_WIDTH-1:0]  dec_info;
	wire dec_rv32;
	wire dec_jal;
	wire dec_rs1x0;
	wire dec_rs2x0;
	wire dec_rs1en;
	wire dec_rs2en;
	wire dec_rdwen;
	wire [4:0] dec_rs1idx;
	wire [4:0] dec_rs2idx;
	wire [4:0] dec_rdidx;
	wire [`E203_XLEN-1:0] dec_imm;
	wire dec_jalr;
	wire dec_bxx;	

	e203_exu_decode u_e203_exu_decode(
	  .i_instr(instruction),
	  .i_pc(`E203_PC_SIZE'b0),
	  .i_prdt_taken(1'b0), 
	  .i_muldiv_b2b(1'b0), 
	  .i_misalgn (1'b0),
	  .i_buserr  (1'b0),
	  .dbg_mode  (1'b0),
	  .dec_rv32(dec_rv32),
	  .dec_rs1x0 (dec_rs1x0),
	  .dec_rs2x0 (dec_rs2x0),
	  .dec_rs1en (dec_rs1en),
	  .dec_rs2en (dec_rs2en),
	  .dec_rdwen (dec_rdwen),
	  .dec_rs1idx(dec_rs1idx),
	  .dec_rs2idx(dec_rs2idx),
	  .dec_rdidx (dec_rdidx),
	  .dec_imm(dec_imm),
	  .dec_jal (dec_jal ),
	  .dec_jalr (dec_jalr ),	  
	  .dec_bxx (dec_bxx ),	  
	  .dec_info  (dec_info )
	);	    
		
    //bxx logic			
    //	
  //bjp group
  wire inst_bjp_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_BJP); 
  wire beq_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BEQ];
  wire bne_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BNE];
  wire blt_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BLT];
  wire bgt_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BGT];
  wire bltu_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BLTU];
  wire bgtu_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BGTU];  
    
  
  
    //
    //Register File block    
	//
    wire[4:0] rf_rs1idx;
    wire[4:0] rf_rs2idx;
    wire[31:0] rf_rs1data;
    wire[31:0] rf_rs2data;
    wire[4:0] rf_rdidx;
    wire rf_rdwen;
    wire[31:0] rf_rd_data;
    wire[31:0] gpio1out;
    
    //register file module
    zh_regfile_v01 m_zh_regfile(
      .rst_n(RST_N),
      .core_clk(core_clk),

      .i_rf_rs1idx(rf_rs1idx),
      .i_rf_rs2idx(rf_rs2idx),
      .rf_rs1data(rf_rs1data),
      .rf_rs2data(rf_rs2data),
      .i_rf_rdidx(rf_rdidx),
      .i_rf_rdwen(rf_rdwen),
      .i_rf_rd_data(rf_rd_data),
      .gpio1out(gpio1out)
    );

    //retrieve reg data
    assign rf_rs1idx={5{dec_rs1en   }} & dec_rs1idx;
    assign rf_rs2idx={5{dec_rs2en   }} & dec_rs2idx;          
      
         
	//
    //alu block
    //       
    wire [31:0] rf_rd_data_alu_result;      
  wire alu_equal_flag;  
  wire alu_less_flag;  
  wire alu_greater_equal_flag;  
    
  //alu group  
  wire inst_alu_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_ALU); 

    wire alu_enable = inst_alu_group | inst_bjp_group;

	wire[31:0] alu_data_a = rf_rs1data;
	wire[31:0] alu_data_b = (inst_alu_group & dec_info[`E203_DECINFO_ALU_OP2IMM]) ? dec_imm : rf_rs2data;
    wire alu_add_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_ADD];
    wire alu_sub_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SUB];    
  wire lui_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_LUI];
  wire auipc_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_OP1PC];
  wire alu_and_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_AND];
  wire alu_or_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_OR];   
  wire alu_xor_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_XOR];
  wire alu_sll_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SLL];   
  wire alu_srl_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SRL];   
  wire alu_sra_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SRA];
  wire alu_slt_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SLT];
  wire alu_sltu_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_SLTU];
  wire alu_compare_flag = alu_slt_flag | alu_sltu_flag | inst_bjp_group;    
  wire alu_unsigned_flag = alu_sltu_flag | bltu_flag | bgtu_flag;
  

	zh_exec_alu_v02 m_zh_exec_alu(
        .i_alu_data_a(alu_data_a),
        .i_alu_data_b(alu_data_b),
        .i_alu_add_flag(alu_add_flag),
        .i_alu_sub_flag(alu_sub_flag),
        .i_alu_enable(alu_enable),        
        .i_alu_and_flag(alu_and_flag),   
        .i_alu_or_flag(alu_or_flag),   
        .i_alu_xor_flag(alu_xor_flag),
        .i_alu_sll_flag(alu_sll_flag),   
        .i_alu_srl_flag(alu_srl_flag),   
        .i_alu_sra_flag(alu_sra_flag),
        .i_alu_compare_flag(alu_compare_flag),
        .i_alu_unsigned_flag(alu_unsigned_flag),   
        .alu_result(rf_rd_data_alu_result),        
        .equal_flag(alu_equal_flag),   //a=b
        .less_flag(alu_less_flag), //a<b
        .greater_equal_flag(alu_greater_equal_flag) //a>=b
    );        
    

 

    //
    //next pc value could be:
    //jal: pc = pc + imm;
    //normal: pc = pc + 4
//    wire [31:0] adder_pc_add_four = pc_r + 32'd4;
//    wire [31:0] adder_pc_add_imm = pc_r + dec_imm;
//    assign pc_next = dec_jal ? adder_pc_add_imm : adder_pc_add_four;    

  wire [31:0] adder_pc_add_four = pc_r + 32'd4;

  wire [31:0] pc_adder_op1 = dec_jalr ? rf_rs1data : pc_r;
  wire [31:0] adder_pc_or_rs1_add_imm = pc_adder_op1 + dec_imm;
  
  //next pc value could be:
  //jal: pc = pc + imm;
  //jalr: pc = i_rf_rs1data + imm;
  //beq: pc = pc + imm if i_alu_equal_flag
  //bne: pc = pc + imm if ~i_alu_equal_flag
  //blt, bltu: pc = pc + imm if i_alu_less_flag
  //bge, bgeu: pc = pc + imm if i_alu_greater_equal_flag
  //ecall, ebreak: not implemented yet
  //mret: pc = mepc, implement later
  //normal: pc = pc + 4  
  
//  wire is_equal =  (alu_data_a == alu_data_b);
  wire is_equal =  alu_enable & alu_equal_flag;
//  wire is_less =  (alu_data_a < alu_data_b);
  wire is_less =  alu_enable & alu_less_flag;
  wire bxx_in_effrect = (beq_flag & is_equal) |   
                        (bne_flag & (~is_equal)) |   
                        ((blt_flag|bltu_flag) & is_less) |
                        ((bgt_flag|bgtu_flag) & (~is_less));                        
                   
  wire bjp_in_effrect = dec_jal | dec_jalr | bxx_in_effrect;


  assign pc_next = bjp_in_effrect ? adder_pc_or_rs1_add_imm : adder_pc_add_four;  
  
  //  
  //load/store block  
  //  
  
  //agu group(load/store)
  wire inst_agu_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_AGU); 
  wire load_flag = inst_agu_group & dec_info[`E203_DECINFO_AGU_LOAD];
  wire store_flag = inst_agu_group & dec_info[`E203_DECINFO_AGU_STORE];
  wire [1:0] lsu_info_size  = dec_info[`E203_DECINFO_AGU_SIZE];
  wire lsu_info_usign = dec_info[`E203_DECINFO_AGU_USIGN];
      
  //ram address  
  wire [31:0] adder_rs1_add_imm = rf_rs1data + dec_imm;    
  
  wire peripheral_field_addr = (adder_rs1_add_imm[31:16] == 16'h4000) ? 1'b1 : 1'b0;
    
  //ram output & input data  
  wire [31:0] load_data_output;    
  wire [31:0] store_data_input;    
  wire [31:0] load_data_output_wb;    
  
  assign store_data_input = lsu_info_size[1] ? rf_rs2data : 
                 lsu_info_size[0] ? (rf_rs2data&32'h0000ffff):  
                                    (rf_rs2data&32'h000000ff);   
                                    
  wire [31:0] load_data_half_sigh ={
                          {16{load_data_output[15]}}
                        , load_data_output[15:0]
                        };                         
  wire [31:0] load_data_half_unsigh ={
                          16'b0
                        , load_data_output[15:0]
                        };                         
  wire [31:0] load_data_byte_sigh ={
                          {24{load_data_output[7]}}
                        , load_data_output[7:0]
                        };                         
  wire [31:0] load_data_byte_unsigh ={
                          24'b0
                        , load_data_output[7:0]
                        };                        
  wire [31:0] load_data_half = lsu_info_usign ? load_data_half_unsigh : 
                                           load_data_half_sigh;   
  wire [31:0] load_data_byte = lsu_info_usign ? load_data_byte_unsigh : 
                                           load_data_byte_sigh;   
                        
  assign load_data_output_wb = lsu_info_size[1] ? load_data_output : 
                 lsu_info_size[0] ? load_data_half : load_data_byte;   
                        
  wire [31:0] peripheral_load_data;    
  wire [31:0] ram_load_data;    
  assign load_data_output = peripheral_field_addr ? peripheral_load_data :  
                                 ram_load_data;    
   
  //use dualport ram for easy monitor    
  //ram from 0x?????000 to 0x?????fff, 1k*32bit
  DualPortIRAM data_ram(
    .clka(clk_96M),    
    .rsta(~RST_N),    
    .ocea(1'b1),    
    .cea((load_flag|store_flag)&(~peripheral_field_addr)),  
    .addra(adder_rs1_add_imm[9:0]),    
    .doa(ram_load_data),    
    .dia(store_data_input),    
    .wea(store_flag),
    
    .clkb(clk_96M),    
    .rstb(~RST_N),    
    .oceb(1'b1),    
    .ceb(1'b0),
    .web(1'b0)
 //   .addrb(pc_value[11:2]),    
 //   .dob(instruction)    
  );      


        
    //write back result
    assign rf_rdidx=dec_rdidx;
    assign rf_rdwen=dec_rdwen;
//    assign rf_rd_data =  alu_enable ? rf_rd_data_alu_result : 32'b0;        

  //rf_rd_data could be:
  // * alu result: add sub, shift, logical; slt/sltu: 1 or 0
  // * lui/auipc: imm or pc + imm
  // * jal/jalr: pc + 4  
  // * load: load_data_output_wb
  //
  assign rf_rd_data = lui_flag ? dec_imm : 
                 (dec_jal | dec_jalr) ? adder_pc_add_four:
                 (auipc_flag) ? adder_pc_or_rs1_add_imm :
                 inst_alu_group ? rf_rd_data_alu_result :   
                 load_flag ? load_data_output_wb : 32'b0; 
    
	
  //led output
	assign LED0 = ~gpio1out[1];
	assign LED1 = ~gpio1out[0];
 //   assign LED2 = 1'b1;	
    
    
    /**	        
     * debug section    
     */        
	wire [31:0] debug_rom_doa;
	wire  [31:0] debug_rom_dia;
	wire  [31:0] debug_rom_addra;	
	wire  [31:0] debug_rom_addra_rd;	
	wire  [31:0] debug_rom_addra_wr;	

	wire  debug_rom_rea;
	wire  debug_rom_wea;
	wire  debug_rom_cea;
	wire  debug_rom_clka;
	wire  debug_rom_rsta;
	wire  debug_rom_ocea;
	
    wire debug_TXD;	
    wire debug_txd_sel;	
    wire core_TXD;    
    assign TXD = debug_txd_sel?debug_TXD:core_TXD;    
    assign LED2 = ~debug_txd_sel;    

  debug_coprocessor_wrapper #(.BAUD_PERIOD (208)) hwloader(    
    .clk(CLK_IN),  //24MHz
    .reset_n(RST_N),    
    .RXD(RXD),
    .TXD(debug_TXD),     
    .debug_uart_tx_sel_ocd1_cpu0(debug_txd_sel),   
    .pram_read_enable_in(1'b1),    
    .pram_read_enable_out(debug_rom_rea),    
    .pram_read_addr_out(debug_rom_addra_rd),    
    .pram_read_data_in(debug_rom_doa),    
    .pram_write_enable_out(debug_rom_wea),    
    .pram_write_addr_out(debug_rom_addra_wr),     
    .pram_write_data_out(debug_rom_dia),    
    .cpu_reset(HW_Loader_Reset)
  );    
    
  assign debug_rom_addra = debug_rom_wea ? debug_rom_addra_wr : debug_rom_addra_rd;    
  assign debug_rom_cea = debug_rom_rea|debug_rom_wea;    
    
  //rom from 0x00000000 to 0x00000fffff, 1k*32bit
  DualPortIRAM debug_iram_high(
    .clka(clk_96M),    
    .rsta(~RST_N),    
    .ocea(1'b1),    
    .cea(debug_rom_cea),  
    .addra(debug_rom_addra[9:0]),    
    .doa(debug_rom_doa),    
    .dia(debug_rom_dia),    
    .wea(debug_rom_wea),
    
    .clkb(clk_96M),    
    .rstb(~RST_N),    
    .oceb(1'b1),    
    .ceb(1'b1),
    .web(1'b0),
    .addrb(pc_value[11:2]),    
    .dob(instruction)    
  );    
    
  //
  //monitor uart    
  //  
/*
    wire tx_pulse;
	assign tx_pulse = (pre_cnt[20:0] == 1) ? 1'b1 : 1'b0;
  
    UART_TX #(.STABLE_TIME(104), .BAUD_PERIOD_BITS(8)) UART_TX_i (
            .clk        (CLK_IN),
            .reset_n    (RST_N),
            .sync_reset (~RST_N),
            
            .start_TX (tx_pulse),
            .baud_rate_period_m1 (8'd207),
            .SBUF_in (pc_value[7:0]),
            .TXD (core_TXD));  
*/            
  //
  //application uart    
  //  
  
  //I/O Register Address:  (0x40000020 - 0x4000003f)
  //  UART0_TX_Data(W)   0x40000020  , write data to least byte
  //  UART0_TX_Send(W)   0x40000024  , write 1 to bit0 to start send
  //  UART0_TX_Idle(R)   0x40000028  , bit0==0 means sending, bit0 == 1 means idle
  //  UART0_RX_Data(R)   0x40000030  , least byte is data received
  //  UART0_RX_Available(R)   0x40000034  , bit0 == 1 means there's data for read
    wire uart0_addr = (adder_rs1_add_imm[15:5] == 11'b00000000001) ? 1'b1 : 1'b0;
    wire uart0_IO_selected = (load_flag|store_flag) & peripheral_field_addr & uart0_addr;    
    wire uart0_tx_data_en = uart0_IO_selected & (adder_rs1_add_imm[4:2] == 3'b000);                         
    wire uart0_tx_send_en = uart0_IO_selected & (adder_rs1_add_imm[4:2] == 3'b001);
    wire uart0_tx_idle_en = uart0_IO_selected & (adder_rs1_add_imm[4:2] == 3'b010);
    wire uart0_rx_data_en = uart0_IO_selected & (adder_rs1_add_imm[4:2] == 3'b100);
    wire uart0_rx_available_en = uart0_IO_selected & (adder_rs1_add_imm[4:2] == 3'b101);
    
    reg [7:0] uart0_tx_data;    
    always@(negedge core_clk) begin
            if (uart0_tx_data_en==1'b1) begin
              uart0_tx_data<=store_data_input[7:0];
            end
        end 
        
    reg uart0_tx_send;           
     
    //core_clk negedge set and posedge clear
    always@(negedge core_clk or posedge core_clk) begin    
          if (core_clk) begin      
              uart0_tx_send<=1'b0;          
          end
          else if (uart0_tx_send_en) begin
            uart0_tx_send<=store_data_input[0];
          end
        end  
        
    wire uart0_tx_idle;    
    reg [7:0] uart0_rx_data;    
    reg uart0_rx_available;     
    
    wire uart0_tx_active;     
    assign uart0_tx_idle = ~uart0_tx_active;  
    
    wire [31:0] uart0_load_data;    
    assign uart0_load_data[31:8] = 24'b0;     
    assign uart0_load_data[7:0] = uart0_tx_idle_en ? {7'b0,uart0_tx_idle} :    
                       uart0_rx_data_en ? uart0_rx_data :    
                       uart0_rx_available_en ? {7'b0,uart0_rx_available} : 8'b0 ;     
   
    
    assign peripheral_load_data = uart0_IO_selected ? uart0_load_data : 32'b0;        

    //uart clk use CLK_IN (24MHz), the uart0_tx_pulse should be 1 CLK_IN period width  	 
    //
/*    
    always@(posedge uart0_tx_send or posedge CLK_IN) begin    
          if (CLK_IN == 1'b1) begin
            if (uart0_tx_pulse == 1'b1) begin
              uart0_tx_pulse<=1'b0;
            end               
          end             
          else begin            
            uart0_tx_pulse<=1'b1;           
          end        

        end	
*/                 

    //for case width of tx_send wider than CLK_IN
    //    
/*
    reg uart0_tx_pulse;               
    reg [1:0] uart0_tx_en_cnt;
    wire uart0_tx_en = (~uart0_tx_en_cnt[1]) & RST_N; //count 2 CLK_IN rise-edge then reset uart0_tx_pulse
    wire uart0_tx_en_clk = CLK_IN & uart0_tx_pulse;        
    
    always@(posedge uart0_tx_send or negedge uart0_tx_en) begin    
          if (uart0_tx_en == 1'b0) 
            uart0_tx_pulse<=1'b0;
          else            
            uart0_tx_pulse<=1'b1;           
        end	       
            
    //2bit counter
    always@(posedge uart0_tx_en_clk or negedge uart0_tx_send) begin    
          if (uart0_tx_send == 1'b0) 
            uart0_tx_en_cnt<=2'b0;
          else            
            uart0_tx_en_cnt<= uart0_tx_en_cnt + 2'b01;           
        end	
*/     
      
    wire tx_pulse;
//	assign tx_pulse = (pre_cnt[20:0] == 1) ? 1'b1 : 1'b0;
	assign tx_pulse = (pre_cnt[12:0] == 1) ? 1'b1 : 1'b0;
//    wire uart0_tx_pulse = tx_pulse & uart0_tx_send;
  	wire uart0_tx_pulse = tx_pulse & uart0_tx_send_en;

    UART_TX #(.STABLE_TIME(104), .BAUD_PERIOD_BITS(8)) UART_TX_0 (
            .clk        (CLK_IN),
            .reset_n    (RST_N),
            .sync_reset (~RST_N),
//            .start_TX (tx_pulse),
//           .start_TX (uart0_tx_send),
           .start_TX (uart0_tx_pulse),
            .baud_rate_period_m1 (8'd207),
            .SBUF_in (uart0_tx_data),            
            .tx_active(uart0_tx_active),
            .TXD (core_TXD));  
            
  

endmodule
